package it.fuesi.synotools.services.tasks.base;

import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Build;
import android.preference.PreferenceActivity;
import android.util.Log;

import java.util.List;

import it.fuesi.synotools.R;
import it.fuesi.synotools.adapters.SynoAdapter;
import it.fuesi.synotools.constants.ApiErrorCode;
import it.fuesi.synotools.constants.ApiErrorKey;
import it.fuesi.synotools.helpers.ClassResolverHelper;
import it.fuesi.synotools.model.auth.LoginResponse;
import it.fuesi.synotools.model.base.ADataObject;
import it.fuesi.synotools.model.base.BaseApiClientParameters;
import it.fuesi.synotools.model.base.ErrorObject;
import it.fuesi.synotools.model.base.SynologyBaseResponse;
import it.fuesi.synotools.services.cache.CacheService;
import it.fuesi.synotools.services.client.LoginClient;
import it.fuesi.synotools.services.client.ServiceClientFactory;

/**
 * Created by pascotto on 17/10/14.
 */
public abstract class BaseTask<T extends SynologyBaseResponse<D>, D extends ADataObject, P extends BaseApiClientParameters> extends AsyncTask<P, String, T> {

    private static final String TAG = "BaseTask";

    private SynoAdapter<D> adapter;
    private ProgressDialog mProgressDialog;
    private Context context;
    private ErrorObject error;

    public ErrorObject getError(){
        return this.error;
    }

    public void setError(ErrorObject error){
        this.error = error;
    }

    protected boolean isCancelable(){
        return false;
    }


    public BaseTask(SynoAdapter<D> adapter){
        super();
        this.adapter = adapter;
        context = adapter.getContext();
    }

    public BaseTask(Context context){
        super();
        this.context = context;
    }




    protected void createProgressDialog()
    {

        if(mProgressDialog != null && mProgressDialog.isShowing())
            mProgressDialog.dismiss();


        //mProgressDialog = ProgressDialog.show(context, "", "Loading...", true);




//        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
//            mProgressDialog = new ProgressDialog(context, R.style.ProgressDialogTheme);
//        else
            mProgressDialog = new ProgressDialog(context);

        mProgressDialog.setMessage(getContext().getString(R.string.loading));
        mProgressDialog.setIndeterminate(true);



        if(isCancelable()) {

            mProgressDialog.setCancelable(true);

            mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {

                public void onCancel(DialogInterface dialog) {
                    cancel(true);
                }

            });
        }

        mProgressDialog.show();


    }

    protected void setAdapter(SynoAdapter<D> adapter){
        this.adapter = adapter;
    }

    public Context getContext(){return this.context;}

    protected SynoAdapter<D> getAdapter(){
        return this.adapter;
    }

    protected abstract String getCacheKey();


    @Override
    protected void onPreExecute() {
        if(ServiceClientFactory.getSynoConfig() == null || !ServiceClientFactory.getSynoConfig().isSet()){
            cancel(true);
            return;
        }
        createProgressDialog();

        super.onPreExecute();
    }

    @Override
    protected void onCancelled() {

        super.onCancelled();
    }


    protected T reauthenticate(){
        publishProgress(getContext().getString(R.string.authenticating));
        final LoginClient client = ServiceClientFactory.getClient(LoginClient.class);
        final LoginResponse response = client.login(ServiceClientFactory.getSynoConfig().getUser(), ServiceClientFactory.getSynoConfig().getPassword()); //FIXME
        if (response.isSuccess()) {
            final String sid = response.getData().getSid();
            Log.d(TAG, "Sid: " + sid);
            CacheService.setSid(getContext(), sid);
        } else {
            Log.e(TAG, "ErrorCode: " + response.getError().getCode());
            this.error = response.getError();

            return null;
        }
        return null;
    }

    @Override
    protected T doInBackground(P... params) {
        try {

            boolean expireCache = false;
            final P apiParameters;
            boolean forceAuthentication = false;

            if (params != null && params.length > 0) {
                apiParameters = params[0];
                if (apiParameters != null) {
                    expireCache = apiParameters.refreshCache();
                    forceAuthentication = apiParameters.isForceAuthentication();
                }
            } else
                apiParameters = null;


            if (ServiceClientFactory.getSynoConfig() == null || !ServiceClientFactory.getSynoConfig().isSet()) {
                final ErrorObject e = new ErrorObject();
                e.setCode(-1);
                e.setMessage("Invalid configuration");

                this.error = e;
                return null;

            }

            Log.d(TAG, "Before authentication --> expireCache["+expireCache+"] - forceAuthentication["+forceAuthentication+"]");


            if(expireCache || !CacheService.hasCache(context, getCacheKey())) {

                if (!CacheService.hasSid(getContext()) || forceAuthentication) {
                    T authResponse = reauthenticate();
                    //if (authResponse != null)
                       // return authResponse;
                }

                T returnObject = run(apiParameters);

                if(!returnObject.isSuccess() && needsAuthentication(returnObject)){
                    Log.w(TAG, "Session ["+CacheService.getSid(getContext())+"] is expired... trying to create a new one");
                    reauthenticate();
                    returnObject = run(apiParameters);
                }
                if(getCacheKey() != null && returnObject.isSuccess())
                    CacheService.writeToCache(getContext(), getCacheKey(), returnObject);



                return returnObject;
            }else {
                Log.d(TAG, "Cache hit --> " + getCacheKey());
                publishProgress(getContext().getString(R.string.loading_from_cache));
                return readFromCache();
            }



        }catch(final Exception e){
            Log.e(TAG, "Generic Exception --> ", e);
            final ErrorObject error = new ErrorObject();
            error.setCode(-1);
            error.setMessage(e.getMessage());

            this.error = error;

            return null;
        }

    }

    private boolean needsAuthentication(T returnObject){
        boolean needsAuthentication = false;
        if(returnObject.getError() != null){
            needsAuthentication =
                    returnObject.getError().getCode() == ApiErrorCode.SessionTimeout.getErrorCode() ||
                    returnObject.getError().getCode() == ApiErrorCode.InvalidPermission.getErrorCode() ||
                    returnObject.getError().getCode() == ApiErrorCode.GuestAccountDisabled.getErrorCode();

        }else if(returnObject.getErrno() != null){
            needsAuthentication =returnObject.getErrno().getKey().equals(ApiErrorKey.NoPrivilege.getErrorKey());
        }
        return needsAuthentication;
    }

    private T readFromCache(){
        Class<T> c = (Class<T>)ClassResolverHelper.getTypeArguments(BaseTask.class, getClass()).get(0);
        return CacheService.readFromCache(context, getCacheKey(), c);
    }



    public abstract T run(P params) throws Exception;

    public void onComplete(T returnObject){

    }




    @Override
    protected void onProgressUpdate(String... values) {
        mProgressDialog.setMessage(values[0]);

        super.onProgressUpdate(values);
    }

    @Override
    protected void onPostExecute(T t) {
        if(t != null){
            if(t.isSuccess()){
                if(adapter != null)
                    adapter.setItemCollection(t.getData());
            }else{
                ErrorObject error = t.getError() != null ? t.getError() : t.getErrno();

                Log.e(TAG, error.getMessage());
                if(error.getCode() > 0 && error.getCode() == ApiErrorCode.InvalidPermission.getErrorCode()){
                    CacheService.setSid(getContext(), null);
                }
                new AlertDialog.Builder(getContext())
                        .setTitle(getContext().getString(R.string.error))
                        .setMessage(error.getMessage())
                        .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                // continue with delete
                            }
                        })
                        .setIcon(android.R.drawable.ic_dialog_alert)
                        .show();
               // Toast.makeText(getContext(), t.getError().getMessage(), Toast.LENGTH_LONG);
            }

        }else if(this.error != null){
            new AlertDialog.Builder(getContext())
                    .setTitle(getContext().getString(R.string.error))
                    .setMessage(this.error.getMessage())
                    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            // continue with delete
                        }
                    })
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .show();
        }


         hideProgress();

        onComplete(t);

        super.onPostExecute(t);
    }

    protected void hideProgress(){
        mProgressDialog.hide();
    }
}
